import json
import os
import socket
import subprocess
import threading
import time
from datetime import datetime
from typing import Dict, List, Optional
import requests
import psutil
import yaml


class Software:
    def __init__(self, name, status=False, path=None, must_start=False):
        self.name = name
        if path is None:
            self.path = ""
        self.status = status
        self.path = path
        self.must_start=must_start
    def __str__(self):
        return f"software(name: {self.name}, status: {self.status}, path: {self.path}, must_start: {self.must_start})"

class Network:
    def __init__(self, name, password, auth = False, auth_url = None, auth_username = None, auth_password = None, check_url = None):
        self.name = name
        self.password = password
        self.auth = auth
        self.auth_url = auth_url
        self.auth_username = auth_username
        self.auth_password = auth_password
        self.check_url = check_url

try:

    def get_func(url, *args, **kwargs):
        resp = requests.get(url, *args, **kwargs)
        return resp


    def post_func(url, data, *args, **kwargs):
        resp = requests.post(url, data=data, *args, **kwargs)
        return resp.text

except ImportError:
    import urllib.request

class AdvancedYAMLObjectReader:
    def __init__(self, yaml_file_path: str):
        self.yaml_file_path = yaml_file_path
        self.objects_dict = {}

    def load_and_parse(self) -> Dict[str, List]:
        """加载并解析YAML文件，返回类实例字典"""
        try:
            with open(self.yaml_file_path, 'r', encoding='utf-8') as file:
                raw_data = yaml.safe_load(file)

            self.objects_dict = {}

            for class_name, objects_list in raw_data.items():
                if class_name == 'software':
                    self.objects_dict[class_name] = [
                        Software(obj['name'], obj['must_start'], obj['path'])
                        for obj in objects_list
                    ]
                elif class_name == 'network':
                    self.objects_dict[class_name] = [
                        Network(obj['name'], obj['password'], obj['auth'], obj['auth_url'], obj['auth_username'], obj['auth_password'], obj['check_url'])
                        for obj in objects_list
                    ]
                else:
                    # 对于未知类，保持为字典
                    self.objects_dict[class_name] = objects_list

            return self.objects_dict

        except Exception as e:
            print(f"错误: {e}")
            return {}



class Detector:

    start=True
    detector_thread=None
    port = 5899  # 控制端口
    host = 'localhost'
    log_file = f"detector日志-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}.log"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'}

    def __init__(self, software=None, network=None):
        if network is None:
            network = []
        if software is None:
            software = []
        self.software = software
        self.network = network
        self.log(f"监视器初始化中....检测目标有{[sw.name for sw in self.software]}")

    def run_command(self, command):
        """执行系统命令并返回结果（修复编码问题）"""
        try:
            result = subprocess.run(
                command,
                capture_output=True,
                text=True,
                encoding='utf-8',  # 先尝试 UTF-8
                errors='ignore',  # 忽略无法解码的字符
                shell=True,
                timeout=10
            )
            return result.stdout
        except UnicodeDecodeError:
            # 如果 UTF-8 失败，尝试其他编码
            try:
                result = subprocess.run(
                    command,
                    capture_output=True,
                    text=True,
                    encoding='gbk',
                    errors='ignore',
                    shell=True,
                    timeout=10
                )
                return result.stdout
            except Exception as e:
                print(f"执行命令失败 (GBK): {e}")
                return None
        except subprocess.TimeoutExpired:
            print(f"命令执行超时: {command}")
            return None
        except Exception as e:
            print(f"执行命令失败: {e}")
            return None

    def detect_software(self):
        # print(self.software)
        for software in self.software:
            self.log(f"正在检测软件: {software.name}, 状态为: {software.status}")
            try:
                software.status = False
                for process in psutil.process_iter(['pid', 'name']):
                    if process.info['name'] and process.info['name'].lower() == software.name.lower():
                        software.status = True
            except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                return False

    def open_software(self):
        for software in self.software:
            if software.status:
                continue
            if software.must_start and software.path:
                self.log(f"正在打开软件: {software.name}")
                subprocess.Popen(software.path, shell=True)
                continue
            if software.must_start:
                print(f"{software.name} 没有指定路径")
                self.log(f"{software.name} 没有指定路径")


    def _encrypt(self, passwd):
        column_key = [0,0,'d','c','j','i','h','g']
        row_key = [
            ['6','7','8','9',':',';','<','=','>','?','@','A','B','C','D','E'],
            ['?','>','A','@','C','B','E','D','7','6','9','8',';',':','=','<'],
            ['>','?','@','A','B','C','D','E','6','7','8','9',':',';','<','='],
            ['=','<',';',':','9','8','7','6','E','D','C','B','A','@','?','>'],
            ['<','=',':',';','8','9','6','7','D','E','B','C','@','A','>','?'],
            [';',':','=','<','7','6','9','8','C','B','E','D','?','>','A','@'],
            [':',';','<','=','6','7','8','9','B','C','D','E','>','?','@','A'],
            ['9','8','7','6','=','<',';',':','A','@','?','>','E','D','B','C'],
            ['8','9','6','7','<','=',':',';','@','A','>','?','D','E','B','C'],
            ['7','6','8','9',';',':','=','<','?','>','A','@','C','B','D','E'],
        ]
        encrypt_passwd = ''
        for idx, c in enumerate(passwd):
            char_c = column_key[ord(c) >> 4]
            char_r = row_key[idx%10][ord(c) & 0xf]
            if idx%2:
                encrypt_passwd += char_c + char_r
            else:
                encrypt_passwd += char_r + char_c
        return encrypt_passwd

    def scan_available_networks(self) -> List[str]:
        """扫描可用的 WiFi 网络"""
        print("正在扫描可用网络...")
        output = self.run_command("netsh wlan show networks mode=bssid")
        available_networks = []

        if output:
            lines = output.split('\n')
            for line in lines:
                line = line.strip()
                # 匹配 SSID 行
                if line.startswith('SSID') and 'BSSID' not in line and ':' in line:
                    parts = line.split(':', 1)
                    if len(parts) > 1:
                        ssid = parts[1].strip()
                        if ssid and ssid not in available_networks:
                            available_networks.append(ssid)

        print(f"扫描到 {len(available_networks)} 个可用网络")
        return available_networks

    def get_current_network(self) -> Optional[str]:
        """获取当前连接的 WiFi 网络名称"""
        output = self.run_command("netsh wlan show interfaces")

        if output:
            lines = output.split('\n')
            for line in lines:
                line = line.strip()
                if line.startswith('SSID') and 'BSSID' not in line and ':' in line:
                    parts = line.split(':', 1)
                    if len(parts) > 1:
                        ssid = parts[1].strip()
                        if ssid and ssid and ssid != "":
                            return ssid
        return "未连接"

    def disconnect_wifi(self) -> bool:
        """断开当前 WiFi 连接"""
        self.log(f"正在断开网络: {self.get_current_network()}")

        output = self.run_command("netsh wlan disconnect")
        time.sleep(3)  # 等待断开完成
        return True  # 即使命令输出不明确，也认为断开成功

    def check_profile_exists(self, name: str) -> bool:
        """检查是否已存在该网络的配置文件"""
        command = f'netsh wlan show profiles name="{name}"'
        output = self.run_command(command)
        return output and "用户配置文件" in output

    def create_wifi_profile(self, network) -> bool:
        """创建 WiFi 配置文件（包含密码）"""
        self.log(f"正在创建wifi {network.name} 的配置文件")
        try:
            # 创建 XML 配置文件
            profile_xml = f'''<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>{network.name}</name>
    <SSIDConfig>
        <SSID>
            <name>{network.name}</name>
        </SSID>
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>auto</connectionMode>
    <MSM>
        <security>
            <authEncryption>
                <authentication>WPA2PSK</authentication>
                <encryption>AES</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
            <sharedKey>
                <keyType>passPhrase</keyType>
                <protected>false</protected>
                <keyMaterial>{network.password}</keyMaterial>
            </sharedKey>
        </security>
    </MSM>
</WLANProfile>'''

            # 将配置文件保存到临时文件
            profile_path = f"C:\\temp\\{network.name}.xml"
            os.makedirs("C:\\temp", exist_ok=True)

            with open(profile_path, 'w', encoding='utf-8') as f:
                f.write(profile_xml)

            # 添加配置文件到系统
            command = f'netsh wlan add profile filename="{profile_path}"'
            output = self.run_command(command)

            if output and "已添加到接口" in output:
                self.log(f"已为网络 {network.name} 创建配置文件")
                # 清理临时文件
                try:
                    os.remove(profile_path)
                except:
                    pass
                return True
            else:
                self.log(f"创建网络配置文件失败: {output}")
                return False

        except Exception as e:
            self.log(f"创建 WiFi 配置文件时发生错误: {e}")
            return False

    def login(self, network):
        self.log(f"正在尝试认证WiFi: {network.name}")
        if not network.auth_username or not network.auth_passwd:
            self.log('认证失败 (账号或密码为空)')
            return False
        encrypt_passwd = self._encrypt(network.auth_passwd)
        payload = {
            'action': 'login',
            'username': network.auth_username,
            'password': encrypt_passwd,
            'type': 2, 'n': 117,
            'drop': 0, 'pop': 0,
            'mbytes': 0, 'minutes': 0,
            'ac_id': 1
            }
        resp_text = post_func(network.auth_url, data=payload, headers=self.headers)
        if 'login_ok' in resp_text:
            self.log('登录成功！')
            self.log(resp_text)
            return True
        elif 'login_error' in resp_text:
            self.log('登录失败')
            self.log(resp_text)
            return False
        else:
            self.log('登录失败（位置失败）')
            self.log(resp_text)
            return False


    def connect_wifi(self):
        available_networks = self.scan_available_networks()
        for network in self.network:
            if network not in available_networks:
                continue
            self.log(f'正在连接网络: {network.name}')
            if not self.check_profile_exists(network.ssid):
                self.log(f"网络 {network.name} 的配置文件不存在")
                self.create_wifi_profile(network)
            command = f'netsh wlan connect name="{network.name}"'
            output = self.run_command(command)
            # 等待连接建立
            time.sleep(5)
            if self.get_current_network() == network.name:
                self.log(f"网络: {network.name} 已经连接")
            online = self.check_online(network)
            if not online:
                self.log(f"WiFi: {network.name} 没有因特网连接")
                result = self.login(network)
                if not result:
                    self.log(f"WiFi: {network.name} 无法认证, 切换下一个网络")
                    continue
            self.log(f"wifi: {network.name} 连接成功")
            break



    def check_online(self, network = None, name = None):
        test_url = "http://www.baidu.com"
        wifi_name = name
        if network:
            test_url = network.auth_url
            wifi_name = network.name
        try:
            self.log(f"正在检查{wifi_name}网络连接, 检测地址: {test_url}")
            resp= get_func(test_url, headers=self.headers)
            if 'not_online' in resp.text:
                return False
            connected = True
            if resp.status_code :
                connected = resp.status_code == 200
            # print(f"connected: {connected}")
            return connected
        except Exception as e:
            print(f"网络连接检查失败: {e}")
            return False

    def detect_network(self):
        if self.check_online(name=self.get_current_network()):
            self.log(f"WiFi: {self.get_current_network()} 网络正常连接")
            return
        self.connect_wifi()



    def detector_start(self, interval=300):
        self.start=True

        def monitor_loop():
            while self.start:
                self.detect_software()
                self.open_software()
                self.detect_network()
                time.sleep(interval)
        self.detector_thread = threading.Thread(target=monitor_loop)
        self.detector_thread.daemon = True
        self.detector_thread.start()
        self.log(f"后台开始监控，检测间隔为:{interval}秒")
    def detector_stop(self):
        self.start=False
        if self.detector_thread:
            self.detector_thread.join(timeout=2)
        self.log(f"停止监控")

    def log(self, message):
        """记录日志"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_entry = f"[{timestamp}] {message}"

        with open(self.log_file, "a", encoding="utf-8") as f:
            f.write(log_entry + "\n")

    def start_control_server(self):
        """启动控制服务器"""
        try:
            server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_socket.bind((self.host, self.port))
            server_socket.listen(1)
            server_socket.settimeout(1)  # 设置超时以便检查运行状态

            self.log(f"控制服务器启动在 {self.host}:{self.port}")

            while self.start:
                try:
                    client_socket, addr = server_socket.accept()
                    self.log(f"接收到控制连接来自 {addr}")

                    # 处理控制命令
                    try:
                        data = client_socket.recv(1024).decode('utf-8').strip()
                        response = self.process_command(data)
                        client_socket.send(response.encode('utf-8'))
                    except Exception as e:
                        self.log(f"处理命令错误: {e}")
                    finally:
                        client_socket.close()

                except socket.timeout:
                    continue
                except Exception as e:
                    self.log(f"服务器错误: {e}")
                    break

            server_socket.close()
            self.log("控制服务器已停止")

        except Exception as e:
            self.log(f"启动控制服务器失败: {e}")

    def process_command(self, command):
        """处理控制命令"""
        command = command.lower()

        if command == "status":
            running_apps = []
            for software in self.software:
                if software.status:
                    running_apps.append(software.name)

            status_info = {
                "service_running": self.start,
                "monitored_apps": len(self.software),
                "running_apps": running_apps,
                "log_file": self.log_file
            }
            return json.dumps(status_info, ensure_ascii=False)

        elif command == "stop":
            self.log("收到停止命令")
            self.detector_stop()
            self.start = False
            return "服务停止命令已发送"


        else:
            return f"未知命令: {command}. 可用命令: status, stop, restart"
    def run(self):
        self.log("=== 后台服务启动 ===")
        self.detector_start()
        self.start_control_server()
        self.log("=== 后台服务停止 ===")

if __name__ == '__main__':
    reader = AdvancedYAMLObjectReader("application.yml")
    objects_dict = reader.load_and_parse()
    detector = Detector(software=objects_dict.get('software', []), network=objects_dict.get('network', []))
    # detector.check_online(detector.network[0])
    print(detector.detect_network())
    # if os.name == 'nt':
    #     import ctypes
    #     ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
    #
    # detector.run()
